package easymodbus4j.example3.sl651;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
import io.netty.handler.codec.LengthFieldPrepender;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;
import io.netty.handler.timeout.IdleStateHandler;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

public class NettyTcpServer {
    // 服务器端口
    private final int port;
    // 业务线程池（分离I/O与业务逻辑）
    private final ExecutorService businessPool = Executors.newFixedThreadPool(
            Runtime.getRuntime().availableProcessors() * 2
    );

    public NettyTcpServer(int port) {
        this.port = port;
    }

    // 启动服务器
    public void start() throws InterruptedException {
        // 1. 配置线程组：Boss（接收连接）+ Worker（处理I/O）
        EventLoopGroup bossGroup = new NioEventLoopGroup(1); // Boss仅需1个线程
        EventLoopGroup workerGroup = new NioEventLoopGroup(); // Worker默认CPU核心数*2

        try {
            // 2. 初始化服务端启动器
            ServerBootstrap bootstrap = new ServerBootstrap();
            bootstrap.group(bossGroup, workerGroup)
                    .channel(NioServerSocketChannel.class) // 基于NIO的服务器通道
                    .option(ChannelOption.SO_BACKLOG, 128) // 服务端队列最大等待连接数
                    .childOption(ChannelOption.SO_KEEPALIVE, true) // 启用TCP心跳（防止连接假死）
                    .childHandler(new ChannelInitializer<SocketChannel>() {
                        // 3. 配置通道处理器（责任链模式）
                        @Override
                        protected void initChannel(SocketChannel ch) {
                            ChannelPipeline pipeline = ch.pipeline();

                            // ① 心跳检测：30秒读空闲（没收到客户端数据）触发心跳检查
                            pipeline.addLast(new IdleStateHandler(30, 0, 0, TimeUnit.SECONDS));
                            // ② 粘包/拆包处理：基于“4字节长度”的编解码器
                            pipeline.addLast(new LengthFieldBasedFrameDecoder(
                                    1024 * 1024, // 最大帧长度（1MB，防止内存溢出）
                                    0, 4, 0, 4   // 前4字节为长度字段，跳过长度字段取数据
                            ));
                            pipeline.addLast(new LengthFieldPrepender(4)); // 发送时自动加4字节长度
                            // ③ 字符串编解码（实际项目可替换为Protobuf/JSON编解码器）
                            pipeline.addLast(new StringDecoder());
                            pipeline.addLast(new StringEncoder());
                            // ④ 自定义业务处理器（核心逻辑）
                            pipeline.addLast(new TcpServerHandler(businessPool));
                        }
                    });

            // 4. 绑定端口，同步等待启动完成
            ChannelFuture future = bootstrap.bind(port).sync();
            System.out.println("TCP服务器已启动，端口：" + port);

            // 5. 监听服务器关闭事件（阻塞，直到服务器主动关闭）
            future.channel().closeFuture().sync();
        } finally {
            // 6. 优雅关闭线程组（释放资源）
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
            businessPool.shutdown();
        }
    }

    // 自定义业务处理器（处理客户端连接、数据收发）
    static class TcpServerHandler extends SimpleChannelInboundHandler<String> {
        private final ExecutorService businessPool;

        public TcpServerHandler(ExecutorService businessPool) {
            this.businessPool = businessPool;
        }

        // 客户端连接建立时触发
        @Override
        public void channelActive(ChannelHandlerContext ctx) {
            String clientAddr = ctx.channel().remoteAddress().toString();
            System.out.println("客户端连接：" + clientAddr);
        }

        // 接收客户端数据时触发（SimpleChannelInboundHandler自动释放数据缓冲区）
        @Override
        protected void channelRead0(ChannelHandlerContext ctx, String msg) {
            String clientAddr = ctx.channel().remoteAddress().toString();
            System.out.println("收到[" + clientAddr + "]数据：" + msg);

            // 提交业务逻辑到独立线程池，避免阻塞I/O线程
            businessPool.submit(() -> {
                try {
                    // 模拟业务处理（如数据库操作、复杂计算）
                    String response = "服务器已收到：" + msg + "（处理耗时：50ms）";
                    TimeUnit.MILLISECONDS.sleep(50);

                    // 回写响应（切换回I/O线程，线程安全）
                    ctx.channel().eventLoop().execute(() -> {
                        ctx.writeAndFlush(response);
                    });
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                }
            });
        }

        // 连接异常时触发
        @Override
        public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
            String clientAddr = ctx.channel().remoteAddress().toString();
            System.out.println("客户端[" + clientAddr + "]连接异常：" + cause.getMessage());
            ctx.close(); // 关闭异常连接
        }

        // 客户端连接断开时触发
        @Override
        public void channelInactive(ChannelHandlerContext ctx) {
            String clientAddr = ctx.channel().remoteAddress().toString();
            System.out.println("客户端[" + clientAddr + "]断开连接");
        }
    }

    // 启动入口
    public static void main(String[] args) throws InterruptedException {
        new NettyTcpServer(8887).start();
    }
}
